﻿#include "timer_tree.hh"
#include "time_util.hh"
#include <limits>

#ifdef max
#undef max
#endif

auto kratos::util::InterTimerNodeCallback::get_name() const noexcept
    -> const std::string & {
  return name_;
}

kratos::util::InterDurationTimer::InterDurationTimer()
    : ts_(0), duration_(0), expired_times_(0), fire_now_(false),
      is_cancel_(false), is_suspend_(false), rest_time_(0) {}

kratos::util::InterDurationTimer::InterDurationTimer(time_t ms,
                                                     time_t duration,
                                                     bool fireNow)
    : ts_(ms), duration_(duration), expired_times_(0), fire_now_(fireNow),
      is_cancel_(false), is_suspend_(false), rest_time_(0) {}

kratos::util::InterDurationTimer::~InterDurationTimer() {}

bool kratos::util::InterDurationTimer::is_expired(time_t ms) {
  if (is_cancel_ || is_suspend_) {
    return false;
  }
  if (rest_time_) {
    if ((ts_ + rest_time_) < ms) {
      rest_time_ = 0;
      // 重置定时器
      reset(ms, duration_, true);
      return true;
    } else {
      return false;
    }
  }
  bool result = ((ts_ + duration_) < ms);
  if (result) {
    expired_times_ += 1;
    ts_ = ms;
  }
  if (fire_now_) {
    fire_now_ = false;
    return true;
  }
  return result;
}

void kratos::util::InterDurationTimer::reset(time_t ms, time_t duration,
                                             bool fireNow) {
  ts_ = ms;
  duration_ = duration;
  fire_now_ = fireNow;
  is_cancel_ = false;
}

time_t kratos::util::InterDurationTimer::get_expired_times() {
  return expired_times_;
}

void kratos::util::InterDurationTimer::cancel() {
  is_cancel_ = true;
  is_cancel_ = true;
  ts_ = 0;
  duration_ = 0;
  is_suspend_ = false;
  rest_time_ = 0;
}

bool kratos::util::InterDurationTimer::cancelled() { return is_cancel_; }

void kratos::util::InterDurationTimer::suspend() {
  is_suspend_ = true;
  auto ts = duration_ - (get_os_time_millionsecond() - ts_);
  if (ts >= duration_) {
    rest_time_ = 0;
  } else {
    rest_time_ = duration_ - ts;
  }
}

void kratos::util::InterDurationTimer::resume() {
  is_suspend_ = false;
  ts_ = get_os_time_millionsecond();
}

bool kratos::util::InterDurationTimer::is_started() { return (0 != ts_); }

kratos::util::InterTimerNode::InterTimerNode(int level, std::time_t duration,
                                             const std::string &name)
    : total_cost_(0), cost_(0), total_run_cb_(0), all_total_cost_(0),
      all_cost_(0), level_(0), name_(name), avg_total_cost_(0), avg_cost_(0),
      run_times_(0), min_cost_(std::numeric_limits<std::time_t>::max()),
      max_cost_(0), min_total_cost_(std::numeric_limits<std::time_t>::max()),
      max_total_cost_(0) {
  // 递增子节点层级
  level_ = level + 1;
  // 建立更新定时器
  timer_.reset(get_os_time_millionsecond(), duration);
}

kratos::util::InterTimerNode::~InterTimerNode() {
  cbs_.clear();
  children_.clear();
}

bool kratos::util::InterTimerNode::add_task_back(CbPtr task) {
  if (!task) {
    return false;
  }
  for (auto it = cbs_.begin(); it != cbs_.end();) {
    if (it->cb.expired()) {
      it = cbs_.erase(it);
    } else {
      if (it->cb.lock() == task) {
        return false;
      } else {
        it++;
      }
    }
  }
  cbs_.push_back(
      {task, 0, 0, true, 0, std::numeric_limits<std::time_t>::max(), 0});
  return true;
}

bool kratos::util::InterTimerNode::add_task_front(CbPtr task) {
  if (!task) {
    return false;
  }
  for (auto it = cbs_.begin(); it != cbs_.end();) {
    if (it->cb.expired()) {
      it = cbs_.erase(it);
    } else {
      if (it->cb.lock() == task) {
        return false;
      } else {
        it++;
      }
    }
  }
  cbs_.push_front(
      {task, 0, 0, true, 0, std::numeric_limits<std::time_t>::max(), 0});
  return true;
}

void kratos::util::InterTimerNode::del_task(CbPtr task) {
  for (auto it = cbs_.begin(); it != cbs_.end();) {
    if (it->cb.expired()) {
      it = cbs_.erase(it);
    } else {
      if (it->cb.lock() == task) {
        cbs_.erase(it);
        return;
      } else {
        it++;
      }
    }
  }
}

kratos::util::ChildPtr
kratos::util::InterTimerNode::add_child_front(std::time_t duration,

                                              const std::string &name) {
  if (!duration) {
    return nullptr;
  }
  ChildPtr child_ptr(new InterTimerNode(level_, duration, name));
  children_.push_front(child_ptr);
  return child_ptr;
}

kratos::util::ChildPtr
kratos::util::InterTimerNode::add_child_end(std::time_t duration,

                                            const std::string &name) {
  if (!duration) {
    return nullptr;
  }
  ChildPtr child_ptr(new InterTimerNode(level_, duration, name));
  children_.push_back(child_ptr);
  return child_ptr;
}

void kratos::util::InterTimerNode::update(std::time_t ms) {
  if (!timer_.is_expired(ms)) {
    return;
  }
  for (auto it = cbs_.begin(); it != cbs_.end();) {
    if (it->cb.expired() || !it->cb.lock()) {
      it = cbs_.erase(it);
      continue;
    }
    if (!it->cb.lock()->do_task()) { // 运行任务回调
      it = cbs_.erase(it);
    } else {
      ++it;
    }
  }
  // 调用子节点循环
  for (auto it = children_.begin(); it != children_.end();) {
    auto &node_ptr = *it;
    if (node_ptr.expired()) {
      it = children_.erase(it);
    } else {
      node_ptr.lock()->update(ms);
      it++;
    }
  }
}

uint32_t kratos::util::InterTimerNode::update_profile(std::time_t ms) {
  if (!timer_.is_expired(ms)) {
    return 0;
  }
  run_times_ += 1;
  total_cost_ = 0;
  cost_ = 0;
  total_run_cb_ = 0;
  for (auto it = cbs_.begin(); it != cbs_.end();) {
    if (it->cb.expired()) {
      it = cbs_.erase(it);
      continue;
    }
    if (!it->result) { // 上次执行时失败， 本次销毁
      it = cbs_.erase(it);
      continue;
    }
    total_run_cb_ += 1;
    bool result = false;
    time_t startUs = util::get_os_time_microsecond();
    if (it->cb.lock()) {
      result = it->cb.lock()->do_task();
    }
    // 任务耗时
    time_t gap = util::get_os_time_microsecond() - startUs;
    cost_ += gap;
    it->cost = gap;
    it->total_cost += gap;
    it->result = result;
    it->run_times += 1;
    if (!result) {
      it->cb.reset();
    }
    if (it->min_cost > it->cost) {
      it->min_cost = it->cost;
    }
    if (it->max_cost < it->cost) {
      it->max_cost = it->cost;
    }
    ++it;
  }
  for (auto it = children_.begin(); it != children_.end();) {
    auto &node_ptr = *it;
    if (node_ptr.expired()) {
      it = children_.erase(it);
    } else {
      auto node_shared_ptr = node_ptr.lock();
      total_run_cb_ += node_shared_ptr->update_profile(ms);
      total_cost_ += node_shared_ptr->get_total_cost();
      it++;
    }
  }
  total_cost_ += cost_;
  all_total_cost_ += total_cost_;
  all_cost_ += cost_;
  if (min_cost_ > cost_) {
    min_cost_ = cost_;
  }
  if (max_cost_ < cost_) {
    max_cost_ = cost_;
  }
  if (min_total_cost_ > total_cost_) {
    min_total_cost_ = total_cost_;
  }
  if (max_total_cost_ < total_cost_) {
    max_total_cost_ = total_cost_;
  }
  return total_run_cb_;
}

void kratos::util::InterTimerNode::get_detail_report(
    std::stringstream &profile) {
  indent(profile) << "Node [" << name_ << "]" << std::endl;
  for (auto it = cbs_.begin(); it != cbs_.end(); it++) {
    if (it->cb.expired() || !it->cb.lock()) {
      continue;
    }
    if (!it->run_times) {
      it->run_times = 1;
    }
    indent(profile) << "  Task [" << it->cb.lock()->get_name()
                    << "] Profile:" << std::endl;
    indent(profile) << "    Current Cost[" << it->cost << "]" << std::endl;
    indent(profile) << "    Avg. Cost[" << it->total_cost / it->run_times << "]"
                    << std::endl;
    indent(profile) << "    Total Run Times[" << it->run_times << "]"
                    << std::endl;
    if (it->min_cost == std::numeric_limits<std::time_t>::max()) {
      indent(profile) << "    MIN Cost[0]" << std::endl;
    } else {
      indent(profile) << "    MIN Cost[" << it->min_cost << "]" << std::endl;
    }
    indent(profile) << "    MAX Cost[" << it->max_cost << "]" << std::endl;
  }
  for (auto it = children_.begin(); it != children_.end(); it++) {
    auto &node_ptr = *it;
    if (node_ptr.expired()) {
      continue;
    } else {
      node_ptr.lock()->get_detail_report(profile);
    }
  }
  if (!run_times_) {
    run_times_ = 1;
  }
  indent(profile) << "Node [" << name_ << "] Profile:" << std::endl;
  indent(profile) << "  Current Cost [" << cost_ << "]" << std::endl;
  indent(profile) << "  Current TotalCost[" << total_cost_ << "]" << std::endl;
  indent(profile) << "  Current Finished Tasks[" << total_run_cb_ << "]"
                  << std::endl;
  indent(profile) << "  Avg. Cost [" << all_cost_ / run_times_ << "]"
                  << std::endl;
  indent(profile) << "  Avg. TotalCost [" << all_total_cost_ / run_times_ << "]"
                  << std::endl;
  if (min_cost_ == std::numeric_limits<std::time_t>::max()) {
    indent(profile) << "  MIN Cost [0]" << std::endl;
  } else {
    indent(profile) << "  MIN Cost [" << min_cost_ << "]" << std::endl;
  }
  indent(profile) << "  MAX Cost [" << max_cost_ << "]" << std::endl;
  if (min_total_cost_ == std::numeric_limits<std::time_t>::max()) {
    indent(profile) << "  MIN Total Cost [0]" << std::endl;
  } else {
    indent(profile) << "  MIN Total Cost [" << min_total_cost_ << "]"
                    << std::endl;
  }
  indent(profile) << "  MAX Total Cost [" << max_total_cost_ << "]"
                  << std::endl;
}

/**
 * 获取详细的性能报告
 */

void kratos::util::InterTimerNode::get_tree_report(std::stringstream &profile) {
  indent(profile) << " |" << std::endl;
  indent(profile) << " + Node [" << name_ << "]" << std::endl;
  for (auto it = cbs_.begin(); it != cbs_.end(); it++) {
    if (it->cb.expired() || !it->cb.lock()) {
      continue;
    }
    if (!it->run_times) {
      it->run_times = 1;
    }
    if (it == cbs_.begin()) {
      indent(profile) << " \\_+Task [" << it->cb.lock()->get_name()
                      << "] Profile:" << std::endl;
    } else {
      indent(profile) << "   +Task [" << it->cb.lock()->get_name()
                      << "] Profile:" << std::endl;
    }
    indent(profile) << "   |_ Current Cost[" << it->cost << "]" << std::endl;
    indent(profile) << "   |_ Avg. Cost[" << it->total_cost / it->run_times
                    << "]" << std::endl;
    indent(profile) << "   |_ Total Run Times[" << it->run_times << "]"
                    << std::endl;
    if (it->min_cost == std::numeric_limits<std::time_t>::max()) {
      indent(profile) << "   |_ MIN Cost[0]" << std::endl;
    } else {
      indent(profile) << "   |_ MIN Cost[" << it->min_cost << "]" << std::endl;
    }
    indent(profile) << "   |_ MAX Cost[" << it->max_cost << "]" << std::endl;
  }
  for (auto it = children_.begin(); it != children_.end(); it++) {
    auto &node_ptr = *it;
    if (node_ptr.expired()) {
      continue;
    } else {
      node_ptr.lock()->get_tree_report(profile);
    }
  }
  if (!run_times_) {
    run_times_ = 1;
  }
  indent(profile) << " _/" << std::endl;
  indent(profile) << "|+ Node [" << name_ << "] Profile:" << std::endl;
  indent(profile) << " \\__ Current Cost [" << cost_ << "]" << std::endl;
  indent(profile) << "  |_ Current TotalCost[" << total_cost_ << "]"
                  << std::endl;
  indent(profile) << "  |_ Current Finished Tasks[" << total_run_cb_ << "]"
                  << std::endl;
  indent(profile) << "  |_ Avg. Cost [" << all_cost_ / run_times_ << "]"
                  << std::endl;
  indent(profile) << "  |_ Avg. TotalCost [" << all_total_cost_ / run_times_
                  << "]" << std::endl;
  if (min_cost_ == std::numeric_limits<std::time_t>::max()) {
    indent(profile) << "  |_ MIN Cost [0]" << std::endl;
  } else {
    indent(profile) << "  |_ MIN Cost [" << min_cost_ << "]" << std::endl;
  }
  indent(profile) << "  |_ MAX Cost [" << max_cost_ << "]" << std::endl;
  if (min_total_cost_ == std::numeric_limits<std::time_t>::max()) {
    indent(profile) << "  |_ MIN Total Cost [0]" << std::endl;
  } else {
    indent(profile) << "  |_ MIN Total Cost [" << min_total_cost_ << "]"
                    << std::endl;
  }
  indent(profile) << "  |_ MAX Total Cost [" << max_total_cost_ << "]"
                  << std::endl;
  indent(profile) << " ____________________________________" << std::endl;
}

/**
 * 获取简略的性能报告
 */

void kratos::util::InterTimerNode::get_brief_report(
    std::stringstream &profile) {
  indent(profile) << "Node [" << name_ << "]" << std::endl;
  for (auto it = cbs_.begin(); it != cbs_.end(); it++) {
    if (it->cb.expired() || !it->cb.lock()) {
      continue;
    }
    if (!it->run_times) {
      it->run_times = 1;
    }
    indent(profile) << "  Task [" << it->cb.lock()->get_name()
                    << "] Profile:" << std::endl;
    indent(profile) << "    Current Cost[" << it->cost << "]" << std::endl;
    indent(profile) << "    Avg. Cost[" << it->total_cost / it->run_times << "]"
                    << std::endl;
  }
  for (auto it = children_.begin(); it != children_.end(); it++) {
    auto &node_ptr = *it;
    if (node_ptr.expired()) {
      continue;
    } else {
      node_ptr.lock()->get_brief_report(profile);
    }
  }
  if (!run_times_) {
    run_times_ = 1;
  }
  indent(profile) << "Node [" << name_ << "] Profile:" << std::endl;
  indent(profile) << "  Current Cost [" << cost_ << "]" << std::endl;
  indent(profile) << "  Current TotalCost[" << total_cost_ << "]" << std::endl;
  indent(profile) << "  Avg. TotalCost [" << all_total_cost_ / run_times_ << "]"
                  << std::endl;
}

auto kratos::util::InterTimerNode::get_total_cost() -> std::time_t {
  return total_cost_;
}

auto kratos::util::InterTimerNode::get_cost() -> std::time_t { return cost_; }

auto kratos::util::InterTimerNode::get_level() -> int {
  return level_;
}

std::stringstream &
kratos::util::InterTimerNode::indent(std::stringstream &profile) {
  for (auto i = 0; i < level_; i++) {
    profile << "  ";
  }
  return profile;
}
